/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
 * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
 * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.alibaba.nacossync.extension.eureka;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.springframework.http.HttpStatus;

import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.google.common.collect.Lists;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpResponse;

import lombok.extern.slf4j.Slf4j;

/**
 * @author liu jun jie
 * @author fenglibin
 * @date 2019-06-26
 */
@Slf4j
public class EurekaNamingService {
    private EurekaHttpClient eurekaHttpClient;
    private EurekaBeatReactor beatReactor;
    //是否将从eureka中获取到的appName属性，在返回给应用之前转换为小写
    private boolean eurekaNameToLowerCase;


    public EurekaNamingService(EurekaHttpClient eurekaHttpClient, boolean eurekaNameToLowerCase) {
        this.eurekaHttpClient = eurekaHttpClient;
        beatReactor = new EurekaBeatReactor(eurekaHttpClient);
        this.eurekaNameToLowerCase = eurekaNameToLowerCase;
    }

    public void registerInstance(InstanceInfo instanceInfo) {
        EurekaHttpResponse<Void> response = eurekaHttpClient.register(instanceInfo);
        if (Objects.requireNonNull(HttpStatus.resolve(response.getStatusCode())).is2xxSuccessful()) {
            beatReactor.addInstance(instanceInfo.getId(), instanceInfo);
        }
    }

    public void deregisterInstance(InstanceInfo instanceInfo) {
        EurekaHttpResponse<Void> response = eurekaHttpClient.cancel(instanceInfo.getAppName(), instanceInfo.getId());
        if (Objects.requireNonNull(HttpStatus.resolve(response.getStatusCode())).is2xxSuccessful()) {
            beatReactor.removeInstance(instanceInfo.getId());
        }
    }

    public List<InstanceInfo> getApplication(String serviceName) {
        EurekaHttpResponse<Application> eurekaHttpResponse =
                eurekaHttpClient.getApplication(serviceName);
        if (Objects.requireNonNull(HttpStatus.resolve(eurekaHttpResponse.getStatusCode())).is2xxSuccessful()) {
        	List<InstanceInfo> instanceList = eurekaHttpResponse.getEntity().getInstances();
        	if(eurekaNameToLowerCase) {
        		instanceAppNameToLowerCase(instanceList);
        	}
            return instanceList;
        }
        return null;
    }
    
    /**
     * 查询单个或多个服务Provider信息，并以应用名称为Key，应用实例列表为Value的Map。
     * @param serviceNames　eurreka中的单个或多个服务的名称，以英文的逗号分隔
     * @return 单个服务对应的服务列表
     */
    public Map<String,List<InstanceInfo>> getApplications(String serviceNames) {
    	Map<String,List<InstanceInfo>> application = new HashMap<String,List<InstanceInfo>>();
    	List<String> serviceNameList = Lists.newArrayList(serviceNames.split(SkyWalkerConstants.COMMA));
    	serviceNameList.forEach(serviceName->{
    		if(eurekaNameToLowerCase) {
    			serviceName = serviceName.toLowerCase();
    		}
    		application.put(serviceName, getApplication(serviceName));
    	});
        return application;
    }
    
    /**
     * 获取Eureka上注册应用信息，并以应用名称为Key，应用实例列表为Value的Map。
     * @return Map的每个Key对应一个服务名称，Value对应该服务的服务列表
     */
    public Map<String,List<InstanceInfo>> getAllApplications(){
    	Map<String,List<InstanceInfo>> applications = new HashMap<String,List<InstanceInfo>>();
    	EurekaHttpResponse<Applications> eurekaHttpResponse = eurekaHttpClient.getApplications(new String[] {});
		List<Application> list = eurekaHttpResponse.getEntity().getRegisteredApplications();
		if (Objects.requireNonNull(HttpStatus.resolve(eurekaHttpResponse.getStatusCode())).is2xxSuccessful()) {
			list.forEach(app->{
				String appName = app.getName();
				List<InstanceInfo> instanceList = app.getInstances();
				if(eurekaNameToLowerCase) {
					appName = appName.toLowerCase();
					instanceAppNameToLowerCase(instanceList);
				}
				
				applications.put(appName, instanceList);
			});
		}
    	return applications;
    }
    
    /**
     * 如果eurekaNameToLowerCase的值为true，则将InstanceInfo中的appName修改为小写
     * @param instanceList
     */
    private void instanceAppNameToLowerCase(List<InstanceInfo> instanceList) {
    	if(!eurekaNameToLowerCase) {
    		return;
    	}
    	if(instanceList==null || instanceList.size()==0) {
    		return;
    	}
    	instanceList.forEach(instance->{
    		instanceAppNameToLowerCase(instance);
    	});
    }
    /**
     * 如果eurekaNameToLowerCase的值为true，则将InstanceInfo中的appName修改为小写
     * @param instance
     */
    private void instanceAppNameToLowerCase(InstanceInfo instance) {
    	if(!eurekaNameToLowerCase) {
    		return;
    	}
    	if(instance==null || instance.getAppName()==null) {
    		return;
    	}
    	try {
			Field appNameField = instance.getClass().getDeclaredField("appName");
			appNameField.setAccessible(true);
			appNameField.set(instance, instance.getAppName().toLowerCase());
		} catch (Exception e) {
			log.error("Modify instanceinfo appName to lowercase exception happened:"+e.getMessage(), e);
		}
    }
}